
// monitor 
// end time of cstl att 
// next time of  hdrin
// spy 30 jobs.. 
em.jobmonitor = new function() {
	this.objName = 'jobmonitor : ';
	this.force_stop =  false;

	var self = this;

	var q =  [];
	
	// ===============================
	// job :  { name:  .. , fn : .... , param: ... }
	// fn : should be return deffered. promise 
	
	function  pushQ (job){
		q.push(job);
	};
	function  pushFirstQ (job){
		q.unshift(job);
	};
	function popQ() {
		return q.shift();
	};
	
	function lengthQ() {
		return q.length;
	};

	function QtoString() {
		var str = '';
		$.each( q, function(_,job) { str += job.name + ',';} );
		return str;
	};

	// priviliged !! 
	function clearQ() {
		q= [];
		self.userOp_doing = false;
		self.force_stop =  false;
		self.job_doing = false;
		debug.log( this, ' cleared Q ~~~~', 'Clear flags');
	};
// 	===================================================================
//         Next time for auto att 
	/*
		next att time
           먼저, cstl end time 보고 받기
              모든 att end time 받자
        현재 cstl end time arr 관리
        hid - endtime -ov 로 cstl 별 시간 관리
    10초 마다,  다음 job check
        cstl 에서 entime 모두 완료된 cstl 찾기
        현재 doing 중이면, queue 에 push
        controller 가 none 으로 변경되면, pop 해서 do start

	*/
  
  
  this.registerCBs = function() {
  	var self = this;
  	// TODO : 이제 지워도 되나?? 
  	// when 1 cstl att done  : started by user cmd 
	em.cbs.autoCstlAttDone.add( function(cstlidx, ret_code) {
		console.log( '====================== cur cstl ' + cstlidx + ' done ... do nothing ');
		} );
		
		
		// add global event cb 
		em.cbs.forcestop.add ( function() { debug.log(self, 'force stop'); clearQ(); 
					self.force_stop = true;
					self.userOp_doing = false;
					self.job_doing = false;});
	}; 


	this.addJob = function(job) {
		if ( job) {
			pushFirstQ(job);
			console.log( job.name + ' added in the Q ');
		}
	}
	

	this.startMonitor = function() {
		var self = this;
		// do first monitor 
		debug.log( this, 'startMonitor !!!!!!!!!!! set timer ');
		this.monitorTimer =  setInterval( function() { self.doMonitor() ; } , 10* 1000);
	};
	
	
	this.IsDoing = function() {
		return this.job_doing;
	};
	
	/*
		 set user operation : block auto start job 
	*/
	this.starUserOperation = function() {
		this.userOp_doing = true;;
	};
	this.endUserOperation = function() {
		this.userOp_doing = false;;
	};
	
	
	// main monitor timer
	// this can be called by timer 
	// or done event 
	this.doMonitor = function() {
		var self = this;

		if (self.force_stop ){
			console.log(' doMonitor. : forced stopped !. ');
			return;
		}
		
		console.log(' IN doMonitor.. : jobs ', q.length, QtoString());
		
/*
			// set next job 
		this.monitorTimer = setTimeout(function(){
				self.doMonitor();
			} , 60 * 1000	);	*/

				
		if ( this.job_doing || this.userOp_doing ) {
			debug.log( this, ' doing  --------',
				 ' userJob', this.userOp_doing  , 
				 	((this.job) ?  this.job.name + ' jobdoing' + this.job_doing  : '' ));
			return;
		}
			
		// pop job when no this.job is doing 
		this.job = popQ();
		// 여기서, next check 뒤에 job 이있다먀녀 , 
		if (this.job) {
			console.log( this.job.name + " start !!");
			
			self.job_doing = true;
			this.job.fn.call(this, this.job.param).always( function() {
					self.job_doing = false;
						console.log(' this.job end : go next ..  ', q.length, QtoString() );
					
					// set next job if no this.job
					if ( lengthQ() == 0) {
						console.log(' JOB is empty ::  put CheckNextJob ');
						em.jobmonitor.addJob( {name: 'checkNextJob', fn: self.checkNextJob, param: [] });					
					} 

				} )
				.done( function() {
						console.log(' donw  ..  cur Job ! ::  timer will start next job ');
					})
				.fail( function() {
					console.log(' cur job  Fail !!  ' );
					
				
				});
		} 
		
	};

	
	// ======================================================
	// job !!! define 
	// deffered  : 
	
	
  //  find next avail cstl 
  this.checkNextJob = function() {
  		var dfd = $.Deferred();

		console.log( 'job : checkNextJob in ', 'this', this);

		if ( em.curOp & em.Operation.AUTOHNT ) {
			// 1 : check next auto att is possible 
			// get next att time 
			var nextTime = em.cstlmgr.getAutoHntNextTime();
			
			em.util.print_result('Auto Att :: Next will be done after : at : ' + 
					nextTime.getHours()+':'+nextTime.getMinutes()+':'+nextTime.getSeconds() );
			
			// em.restartAttTimer = setTimeout ("em.hntmgr.startAuto()" , timeout );
			if ( nextTime.getTime() < $.now() ) {
				em.util.print_result( 'Start now job :  StartCstlHnt' + new Date() );
			
				em.jobmonitor.addJob( {name: 'StartCstlHnt', fn: em.jobmonitor.startAutoHnt, param: [ em.jobmonitor.att_count --] });
				dfd.resolve();
				return dfd.promise();
			} 
			
		}
			

		
		
		// 2 : check next hdrink job 
		if ( em.curOp & em.Operation.HDRIN ) {
			var nextTime = em.hdrinmgr.getNextCheckTime();
			
			em.util.print_result('hdrin  :: Next will be done after : at : ' + 
					nextTime.getHours()+':'+nextTime.getMinutes()+':'+nextTime.getSeconds() );
			
			// em.restartAttTimer = setTimeout ("em.hntmgr.startAuto()" , timeout );
			if ( nextTime.getTime() < $.now() ) {
				em.util.print_result( 'Start now job :  StartCstlHnt' + new Date() );
			
				em.jobmonitor.addJob( {name: 'doHDrin', fn: em.jobmonitor.doHDrin, param: [] });
				dfd.resolve();
				return dfd.promise();
			} 
		}
		
		// 2 : check next buy sp30  job 
		if ( em.curOp & em.Operation.BUYSP30 ) {
			if ( ! em.jobmonitor.sp30 ) {
				em.jobmonitor.sp30 = {};
				em.jobmonitor.sp30.checkCnt = 0;
			}
			
				em.util.print_result('SP30 :: trade cnt: : ' +  em.jobmonitor.sp30.checkCnt );
			// every  each 10 * 20sec    
			if ( em.jobmonitor.sp30.checkCnt++ > 10 ) {
				em.jobmonitor.sp30.checkCnt = 0;
				
				var tem = CDB.SP30.getTemInfo();
				em.util.print_result('SP30 :: find trade : ' + tem.server + ' sid:' + tem.sid );

				debug.log( em.jobmonitor, ' BUY SP30 : Tem info  : ', tem);
				
				em.jobmonitor.addJob( {name: 'BUYSP30', fn: em.jobmonitor.doBuySP30, param: tem });
				
				dfd.resolve();
				return dfd.promise();				
			}
		}

		// : check enhance  
		if ( em.curOp & em.Operation.ENHANCE ) {
			// after once done enhance.. 
			if (em.controller.doEnhance  && em.controller.doEnhance.nexttime ) {
				
				var nextTime = em.controller.doEnhance.nexttime;
				
				if ( nextTime == -1 ) {
					// cancel job 
					em.curOp &= ~em.Operation.ENHANCE;
					dfd.resolve();
					return dfd.promise();
				}
				em.util.print_result('ENHANCE  :: Next will be done after : at : ' + 
						nextTime.getHours()+':'+nextTime.getMinutes()+':'+nextTime.getSeconds() );
				
				// em.restartAttTimer = setTimeout ("em.hntmgr.startAuto()" , timeout );
				if ( nextTime.getTime() < $.now() ) {
					em.util.print_result( 'Start now job :  Enhance ' + new Date() );
				
					em.jobmonitor.addJob( {name: 'DoEnhance ', fn: em.jobmonitor.doEnhance, param: 'redo' });
					
					dfd.resolve();
					return dfd.promise();
				} 
			}
		}
		
		
		// each 5  min : check   mine nextdate , when near end : start hnt  
		if ( em.curOp & em.Operation.AVOID_PV ) {

			console.log( 'AVOID PV check', this);
			// after 5 min 
			if ( $.now()  > em.jobmonitor.avoid_pv.lastcheck + 60*1000*2  ){
				em.jobmonitor.avoid_pv.lastcheck = $.now();
				
				em.util.print_result( 'Start now job :  AVOID_PV :: start again' + CUtil.TimeToString(new Date()) );
				em.jobmonitor.addJob( {name: 'Avoid_PV', fn: em.jobmonitor.checkInterval_PVP, param: [] });
				dfd.resolve();
				return dfd.promise();
			} 
		}
		
		
		// check next dur .
		if ( em.curOp & em.Operation.DUR_PV ) {
			 // get nexttime of dur ..
			var nextTime;   
			if (! em.controller.dur) {
				nextTime = new Date($.now() - 10000);		// start right now   
			} else {
				nextTime = em.controller.dur.endtime;
			}
			
			em.util.print_result('UP DUR :: Next will be done after : at : ' + 
					nextTime.getHours()+':'+nextTime.getMinutes()+':'+nextTime.getSeconds() );
			
			// em.restartAttTimer = setTimeout ("em.hntmgr.startAuto()" , timeout );
			if ( nextTime.getTime() < $.now() ) {
				em.util.print_result( 'Start now job :  Start UP Dur' + CUtil.TimeToString(new Date()) );
			
				em.jobmonitor.addJob( {name: 'Start UpDur_PVP', fn: em.jobmonitor.UpDur_PVP, param: [] });
				dfd.resolve();
				return dfd.promise();
			} 
		}
		
			//  last ..  : sp 30 action ...  each time.   always do.
		if ( em.curOp & em.Operation.SP30ACTION ) {
					debug.log( this, ' SP30ACTION :   start .. next  : ');
			// em.restartAttTimer = setTimeout ("em.hntmgr.startAuto()" , timeout );
			
				em.jobmonitor.addJob( {name: 'SP30ACTION', fn: em.jobmonitor.doSP30Action, param: undefined });
				dfd.resolve();
				return dfd.promise();
		}
		// do next job 
		dfd.resolve();	
  		return dfd.promise();
  };
	
	  //  find next avail cstl 
  this.startAutoHnt = function(arg) {
  		var dfd = $.Deferred();

		em.jobmonitor.att_count  = arg[0];
		if ( em.jobmonitor.att_count  <= 0 ) {
			em.curOp ^= em.Operation.AUTOHNT;
			console.log( 'job : startAutoHnt  Req Cnt finished..!!! ', this , 'att_count::', em.jobmonitor.att_count);
	  		return dfd.promise();	
		}
		
		console.log( 'job : startAutoHnt in ', this , 'att_count::', em.jobmonitor.att_count);

  		// go start 		 		
		// test !!!  
  		em.controller.doAutoHnt (0, dfd);
  		return dfd.promise();
  };
	

	// ... hdrin !! job  
	this.doHDrin =  function (cstl_idx) {
		var dfd = $.Deferred();
		console.log( 'job : doHDrin in ', this);		// this, <- object of doHDrin

		em.controller.doHDrin (dfd);
				
  		return dfd.promise();
		
	};
	
	
	this.doSP30Action =  function ( arg ) {
		var dfd = $.Deferred();
		console.log( 'job : doSP30Action in '); 

		em.controller.doSP30Action(dfd);
				
		$.when( dfd).then ( function() {
			debug.log( this, 'Sucess in 						doSP30Action  jobmonitor ');
			}, 
			function() {
				
				debug.log( this, 'fail in 						doSP30Action  jobmonitor ');
			});
  		return dfd.promise();
		
	};

	this.doBuySP30 =  function ( tem ) {
		var dfd = $.Deferred();
		console.log( 'job : SP30 in ', tem); 

		em.controller.doBuySP30 (tem, dfd);
				
  		return dfd.promise();
		
	};
	

 //  Scan & Result  => make DA arr
  this.searchDAsArr = function() {
  		var dfd = $.Deferred();

		console.log( 'job : scanDAsArr in ', this);

  		// do world _ > cur user :  => make Da Array  [][][]
  		//  amd . sort  by dist from user x,y , print result 
  		if (! em.worldDA) 
  			em.worldDA = new CWorldDA();
  		
  		
  		var self = em.jobmonitor;	
		em.worldDA.startWorldScan()
			.done(function(){
						 
				debug.log( self,  ' Done !!! ');
				
				// do next job : scan 
				self.addJob( {name: 'StartCstlHnt next', fn: self.scanDAsArr, param: [] });
				dfd.resolve();
			});
	
 		
  		return dfd.promise();
  };
  
  
  
 	// do every 15 min 
   this.doEnhance  = function(arg) {
  		var dfd = $.Deferred();
  		
		console.log( 'job : doEnhance in ', this); // <= doEnhance object  
	
 	  	// go start 		 		
  		em.controller.doEnhance(arg,dfd);
  		return dfd.promise();
  };
  
  // send scout & wait endtime & get result
  // print make array  => manual copy by user to js data file  
  this.scanDAsArr  = function(args) {
  		var dfd = $.Deferred();
  		
		console.log( 'job : scanDAsArr in ', this);
  		
/*
  		
  		var self = em.jobmonitor;	
  		em.controller.doScanDAsArr(args,dfd)
			.always(function( longest_traveltime){
						 
				debug.log( self,  ' Done !!! ');
				
				// do next job :  check result &  add fav 
				// set timer :  end time of travel time ( one -way time )
				// setTimeout( function() { em.controller.checkResultAddFav( dfd); } , travel_secs * 1000);
				setTimeout( function() { em.jobmonitor.addJob( {name: 'checkResultAddFav', fn: em.jobmonitor.checkResultAddFav, param: [] });
									} , (longest_traveltime.getTime() - $.now()) );
				dfd.resolve();
			});*/

	
 	  	// go start 		 		
  		em.controller.doScanDAsArr(args,dfd);
  		return dfd.promise();
  };
	
 // send scout & wait endtime & get result
  // print make array  => manual copy by user to js data file  
  this.checkResultAddFav  = function() {
  		var dfd = $.Deferred();

		console.log( 'job : checkResultAddFav in ', this);
/*
  		
  		var self = em.jobmonitor;	
  		em.controller.checkResultAddFav(args)
			.done(function(  ){
						 
				debug.log( self,  ' Done !!! ');
				
				// done 
				dfd.resolve();
			});*/

 		em.controller.checkResultAddFav(dfd);
  		return dfd.promise();
  };
		
 //  grow cstl 
 // train,   study, build  ..  order job is defined by each user option 
  this.growing = function() {
  		var dfd = $.Deferred();

		console.log( 'job : growing in ', this);

  		// 1. check all cstls end time  & get shortest cstl
  		
  		// check possible for cur att 
  		if ( _shortest_cstl.endtime < $.now() ) {
	  		// 2. push job Q with cstl att cmd ( index)
	  		var curidx = _shortest_cstl.index < 0 ?  0 : _shortest_cstl.index;
	  		
	  		// 이게 auto att  기ㅏ  job 이 아니고,  현재  함수가 job 이니까,,   그냥 수행 하면 됨. 
	  		// pushQ ( {name: 'AutoHntAtt' + curidx ,  fn : em.controller.doAutoHnt , param: [curidx, dfd] }  );
	  		em.controller.doAutoHnt (curidx, dfd);
  			
  		}
  		//  put  job for next checking 
  		 else {
			 pushQ ( { name: 'Check HntAtt', fn : em.jobmonitor.checkNextAtt , param: [] }  ); 	
			 dfd.resolve();		// go next job 
  		}
  		return dfd.promise();
  };
  
  
  
  
  
  
  
  
  
    this.UserVScan_PVP = function() {
  		var dfd = $.Deferred();

		console.log( 'job : UserVScan_PVP in ', this);

		em.worldDA.scanWorldVictom_PVP()
			.done(function(){
				debug.log( self,  'scanWorldVictom_PVP Done !!! ');
				dfd.resolve();
				
				em.cstlview.showUsPanel();
			})
			.fail(function(){
				debug.log( self,  'scanWorldVictom_PVP Fail !!! ');
				dfd.reject();
			});  	
				
  		return dfd.promise();
  };
  
   this.lootUs_PVP = function(args) {
  		var dfd = $.Deferred();

		console.log( 'job : lootUs_PVP in ', this);		// this, <- object of lootUs_PVP

		// start before 10 sec 
		
		var timediff =  args[0].nextdate.getTime() - $.now();
		
		console.log( 'time: diff ', timediff, ' us time',  CUtil.TimeToString(args[0].nextdate) 
						, ' now ', CUtil.TimeToString(new Date()));
		
		if ( timediff > 4 * 1000 ) {
			setTimeout( function() { em.controller.doLootUs_PVP(args, dfd); } , timediff - 4*1000  );
		} else if ( timediff < - 3*60*10000  || timediff > 15 * 60 * 1000 ) {
			// invalid time.
			debug.log(this, ' time is invalid or passed' , timediff, $.now() , args[0].nextdate ) ;
			dfd.reject();
		} else {
			// start right now
			console.log( 'job : lootUs_PVP in  -- start Right now '); 
			em.controller.doLootUs_PVP (args, dfd);
		}
				
  		return dfd.promise();
  };
  
   this.UpDur_PVP = function() {
  		var dfd = $.Deferred();

		console.log( 'job : lootUs_PVP in ', this);

		em.controller.UpDur_PVP(dfd);
				
  		return dfd.promise();
  };
  
   this.checkInterval_PVP = function() {
  		var dfd = $.Deferred();

		console.log( 'job : checkInterval_PVP in ', em.jobmonitor);

		// craete check timer 
		if ( ! em.jobmonitor.avoid_pv ) { 
				em.jobmonitor.avoid_pv = {} ;
				em.jobmonitor.avoid_pv.lastcheck = $.now();
				}
				
		em.controller.checkInterval_PVP(dfd);
				
  		return dfd.promise();
  };
  
  

  			

	
};